
from collections import namedtuple

Customer = namedtuple('Customer', 'name fidelity')


class LineItem:
    def __init__(self, product, quantity, price) -> None:
        self.product = product
        self.quantity = quantity
        self.price = price
    def total(self):
        return self.price * self.quantity

class Order:
    def __init__(self, customer, cart, promotion=None) -> None:
        self.customer = customer
        self.cart = list(cart)
        self.promotion = promotion
    def total(self):
        if not hasattr(self, '_total'):
            self._total = sum(item.total() for item in self.cart)
        return self._total
    def due(self):
        if self.promotion is None:
            discount = 0
        else:
            discount = self.promotion(self)
        return self.total() - discount
    def __repr__(self):
        fmt = '<Order total:{:.2f} due:{:.2f}>'
        return fmt.format(self.total(), self.due())

promos = []
def promotion(promo_func):
    promos.append(promo_func)
    return promo_func
@promotion
def fidelity_promo(order):
    return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0
@promotion
def bulk_item_promo(order):
    discount = 0
    for item in order.cart:
        if item.quantity >= 20:
            discount += item.total() * .1
    return discount
@promotion
def large_order_promo(order):
    distinct_items = {item.product for item in order.cart}
    if len(distinct_items) >= 10:
        return order.total() *.07
    return 0

def best_promo(order):
    print(promos)
    return max(promo(order) for promo in promos)

if __name__ == '__main__':
    jeo = Customer('Joe', 0)
    ann = Customer('Ann', 1100)
    cart = [LineItem('banana', 4, .5),LineItem('apple', 10, 1.5), LineItem('water', 5, 5.0)]
    print(Order(jeo, cart, best_promo))
    # print(Order(ann, cart, FidelityPromo()))
    # bana_cart = [LineItem('banana', 30, .5), LineItem('apple', 10, 1.5)]
    # print(Order(jeo, bana_cart, BulkItemPromo()))